#!/bin/bash
# Mac-Installer für Spieler (steckt als Contents/MacOS/Schuetzenfest in „Schuetzenfest einrichten.app“,
# gebaut von installer/baue.sh). Modpack, Bilder und Oberfläche liegen in Contents/Resources/.
#   ohne Argumente: zeigt das Fenster (oberflaeche.js); geht das nicht, schlichte Dialoge
#   --arbeiter <modrinth|launcher> <Statusdatei>: die eigentliche Arbeit, Fortschritt als „<Prozent>|<Text>“
#   Modrinth App: suchen oder aktuelle Version laden und nach ~/Applications legen, dann das .mrpack importieren.
#   Ohne Modrinth: offizieller Minecraft Launcher mit Fabric-Version, eigenem Spielordner und Profil „Schützenfest“.
set -u
SKRIPT="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
HIER="$(cd "$(dirname "$0")/../Resources" && pwd)"
MRPACK="$(ls "$HIER"/Schuetzenfest-*.mrpack 2>/dev/null | head -n1)"
AGENT="SchapfeldNils/schuetzenfest-mod (Spieler-Installer)"
LOG="$HOME/Library/Logs/Schuetzenfest-Installer.log"
mkdir -p "$(dirname "$LOG")"
exec >>"$LOG" 2>&1
echo "=== $(date) $* ==="
ARBEITER=""; STATUS=""
if [ "${1:-}" = "--arbeiter" ]; then ARBEITER="$2"; STATUS="$3"; fi

status() { # Prozent, Text
    echo "$1|$2"
    if [ -n "$STATUS" ]; then echo "$1|$2" >> "$STATUS"; fi
}

meldung() { # Text, [Symbol]
    [ -n "$ARBEITER" ] && return 0
    osascript - "$1" "${2:-note}" <<'OSA' >/dev/null
on run argv
  display dialog (item 1 of argv) with title "Schützenfest" buttons {"OK"} default button "OK" with icon (item 2 of argv as text)
end run
OSA
}
fehler() { # Text, [Exit-Code]
    echo "Fehler: $1"
    if [ -n "$ARBEITER" ]; then status -1 "$1"; exit "${2:-1}"; fi
    meldung "$1"$'\n\n'"Protokoll: $LOG" stop; exit "${2:-1}"
}
hinweis() { # kurzer Hinweis oben rechts, blockiert nicht
    [ -n "$ARBEITER" ] && return 0
    osascript -e "display notification \"$1\" with title \"Schützenfest\"" >/dev/null 2>&1 || true
}

[ -f "$MRPACK" ] || fehler "Das Modpack fehlt in der App. Bitte den Installer neu herunterladen."
VERSION="$(basename "$MRPACK" .mrpack)"; VERSION="${VERSION#Schuetzenfest-}"

if [ -z "$ARBEITER" ]; then
    # Das schicke Fenster; klappt es nicht (altes macOS, Ausnahme), geht es mit Dialogen weiter
    if osascript -l JavaScript "$HIER/oberflaeche.js" "$HIER" "$SKRIPT" "$VERSION"; then exit 0; fi
    echo "Fenster ging nicht, nehme Dialoge."
fi

if [ -n "$ARBEITER" ]; then
    case "$ARBEITER" in modrinth) WAHL="Modrinth App" ;; *) WAHL="Minecraft Launcher" ;; esac
else
WAHL="$(osascript <<OSA
button returned of (display dialog "Schützenfest $VERSION einrichten: Minecraft 26.3 mit Fabric, allen Mods, Schützenfest-Shader und dem Server in der Serverliste.

Mit der Modrinth App (empfohlen, wird bei Bedarf installiert) oder mit dem offiziellen Minecraft Launcher?" with title "Schützenfest" buttons {"Abbrechen", "Minecraft Launcher", "Modrinth App"} default button "Modrinth App" cancel button "Abbrechen")
OSA
)" || exit 0
fi
echo "Wahl: $WAHL"

lade() { curl -fsSL --retry 3 -A "$AGENT" "$@"; }

# ---------------------------------------------------------------- Modrinth App
finde_modrinth() {
    for p in "/Applications/Modrinth App.app" "$HOME/Applications/Modrinth App.app"; do
        [ -d "$p" ] && { echo "$p"; return 0; }
    done
    mdfind "kMDItemCFBundleIdentifier == 'ModrinthApp'" 2>/dev/null | head -n1
}

mit_modrinth() {
    status 5 "Suche die Modrinth App …"
    APP="$(finde_modrinth)"
    if [ -z "$APP" ]; then
        hinweis "Lade die Modrinth App …"
        status 15 "Lade die Modrinth App herunter …"
        case "$(uname -m)" in arm64) PLATTFORM=darwin-aarch64 ;; *) PLATTFORM=darwin-x86_64 ;; esac
        URL="$(lade https://launcher-files.modrinth.com/updates.json | osascript -l JavaScript -e '
            function run() { const d = JSON.parse($.NSString.alloc.initWithDataEncoding($.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile, $.NSUTF8StringEncoding).js);
              const p = d.platforms["'"$PLATTFORM"'"] || d.platforms["darwin-universal"]; return p ? p.url : ""; }')"
        echo "Modrinth App: $URL"
        TMP="$(mktemp -d)"
        if [ -n "$URL" ] && lade -o "$TMP/app.tar.gz" "$URL"; then
            status 55 "Installiere die Modrinth App …"
            mkdir -p "$HOME/Applications"
            # Das Update-Archiv enthält die fertige „Modrinth App.app“
            case "$URL" in *.tar.gz) tar -xzf "$TMP/app.tar.gz" -C "$HOME/Applications" ;; esac
        fi
        APP="$(finde_modrinth)"
    fi
    if [ -z "$APP" ]; then
        open "https://modrinth.com/app"
        fehler "Die Modrinth App ließ sich nicht automatisch installieren. Im Browser ist modrinth.com/app geöffnet: dort installieren und dann nochmal versuchen – oder den Weg über den Minecraft Launcher nehmen." 2
    fi
    echo "Modrinth App: $APP"
    status 85 "Übergebe das Modpack an die Modrinth App …"
    ZIEL="$HOME/Library/Application Support/Schuetzenfest"
    mkdir -p "$ZIEL"
    cp -f "$MRPACK" "$ZIEL/"
    PACK="$ZIEL/$(basename "$MRPACK")"
    if ! open -a "$APP" "$PACK"; then
        open -R "$PACK"
        fehler "Die Modrinth App hat das Modpack nicht angenommen. Bitte die markierte Datei in das Fenster der Modrinth App ziehen." 3
    fi
    status 100 "Fertig!"
    meldung "Die Modrinth App öffnet sich mit dem Modpack:

1. Den Import bestätigen und warten, bis das Profil „Schützenfest“ fertig ist.
2. Mit dem Microsoft-Konto anmelden.
3. Beim Profil „Schützenfest“ auf Spielen, dann Mehrspieler → Schützenfest.

Beim ersten Beitreten lädt der Server fehlende Teile nach und startet das Spiel einmal neu."
}

# ---------------------------------------------------------------- Offizieller Launcher
mit_launcher() {
    MC="$HOME/Library/Application Support/minecraft"
    if [ ! -f "$MC/launcher_profiles.json" ]; then
        open "https://www.minecraft.net/download"
        fehler "Der offizielle Minecraft Launcher ist noch nicht eingerichtet. Im Browser ist minecraft.net geöffnet: Launcher installieren, einmal anmelden und dann nochmal versuchen." 4
    fi
    hinweis "Richte Minecraft ein, das dauert ein paar Minuten …"
    status 3 "Packe das Modpack aus …"
    SPIEL="$MC/schuetzenfest"
    TMP="$(mktemp -d)"
    unzip -q -o "$MRPACK" -d "$TMP/pack" || fehler "Modpack lässt sich nicht entpacken."
    # Alles Weitere (JSON lesen und schreiben) mit JavaScript for Automation, das jeder Mac mitbringt
    osascript -l JavaScript - "$TMP/pack" "$SPIEL" "$MC" "$AGENT" "$STATUS" <<'JXA' || fehler "Einrichten fehlgeschlagen. Prüfe die Internetverbindung."
ObjC.import('Foundation');
function lies(p) { return $.NSString.stringWithContentsOfFileEncodingError(p, $.NSUTF8StringEncoding, null).js; }
function schreib(p, t) { $.NSString.alloc.initWithUTF8String(t).writeToFileAtomicallyEncodingError(p, true, $.NSUTF8StringEncoding, null); }
function sh(cmd) { const a = Application.currentApplication(); a.includeStandardAdditions = true; return a.doShellScript(cmd); }
function q(s) { return "'" + s.replace(/'/g, "'\\''") + "'"; }
function ordner(p) { $.NSFileManager.defaultManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(p, true, $(), null); }
function existiert(p) { return $.NSFileManager.defaultManager.fileExistsAtPath(p); }
function sicher(basis, rel) {
  if (rel.startsWith('/') || rel.split('/').includes('..')) throw new Error('Ungültiger Pfad im Modpack: ' + rel);
  return basis + '/' + rel;
}
function run(argv) {
  const [pack, spiel, mc, agent, status] = argv;
  const melde = (p, t) => { console.log(t); if (status) sh('printf "%s\\n" ' + q(p + '|' + t) + ' >> ' + q(status)); };
  const index = JSON.parse(lies(pack + '/modrinth.index.json'));
  const mcV = index.dependencies.minecraft, loader = index.dependencies['fabric-loader'];
  melde(5, 'Richte Minecraft ' + mcV + ' mit Fabric ' + loader + ' ein …');
  // 1. Fabric-Version wie der Fabric-Installer; Bibliotheken lädt der Launcher selbst
  const fabric = sh('curl -fsSL --retry 3 -A ' + q(agent) + ' ' + q('https://meta.fabricmc.net/v2/versions/loader/' + mcV + '/' + loader + '/profile/json'));
  const versionId = JSON.parse(fabric).id;
  ordner(mc + '/versions/' + versionId);
  schreib(mc + '/versions/' + versionId + '/' + versionId + '.json', fabric);
  melde(10, 'Fabric-Version ' + versionId + ' angelegt');
  // 2. Mods laden (nur Spieler-Seite), SHA-1 prüfen
  const soll = {};
  let nr = 0;
  for (const f of index.files) {
    nr++;
    if (f.env && f.env.client === 'unsupported') continue;
    const ziel = sicher(spiel, f.path);
    soll[f.path.split('/').pop()] = true;
    ordner(ziel.substring(0, ziel.lastIndexOf('/')));
    const pruef = () => existiert(ziel) && sh('shasum -a 1 ' + q(ziel) + " | cut -d' ' -f1") === f.hashes.sha1;
    if (pruef()) continue;
    melde(10 + Math.round(70 * nr / index.files.length), 'Lade ' + f.path.split('/').pop() + ' …');
    sh('curl -fsSL --retry 3 -A ' + q(agent) + ' -o ' + q(ziel) + ' ' + q(f.downloads[0]));
    if (!pruef()) throw new Error('Prüfsumme falsch: ' + f.path);
  }
  melde(82, 'Packe Schützenfest-Mod, Shader und Serverliste aus …');
  // 3. overrides/ und client-overrides/ (eigene Einstellungen und Serverliste nicht überschreiben)
  for (const praefix of ['overrides', 'client-overrides']) {
    const quelle = pack + '/' + praefix;
    if (!existiert(quelle)) continue;
    const liste = sh('cd ' + q(quelle) + ' && find . -type f').split('\r').join('\n').split('\n').filter(x => x);
    for (const roh of liste) {
      const rel = roh.replace(/^\.\//, '');
      const ziel = sicher(spiel, rel);
      if ((rel === 'servers.dat' || rel.startsWith('config/')) && existiert(ziel)) continue;
      if (rel.startsWith('mods/')) soll[rel.split('/').pop()] = true;
      ordner(ziel.substring(0, ziel.lastIndexOf('/')));
      sh('cp -f ' + q(quelle + '/' + rel) + ' ' + q(ziel));
    }
  }
  // alte Mod-Versionen nach einem Update abschalten (umbenennen)
  const mods = $.NSFileManager.defaultManager.contentsOfDirectoryAtPathError(spiel + '/mods', null).js.map(x => x.js);
  for (const m of mods) if (m.endsWith('.jar') && !soll[m]) sh('mv -f ' + q(spiel + '/mods/' + m) + ' ' + q(spiel + '/mods/' + m + '.alt'));
  melde(92, 'Trage das Profil „Schützenfest“ im Minecraft Launcher ein …');
  // 4. Launcher-Profil eintragen, andere Profile bleiben
  const pfad = mc + '/launcher_profiles.json';
  sh('cp -f ' + q(pfad) + ' ' + q(pfad + '.vor-schuetzenfest'));
  const daten = JSON.parse(lies(pfad));
  daten.profiles = daten.profiles || {};
  const jetzt = new Date().toISOString();
  daten.profiles.schuetzenfest = {
    name: 'Schützenfest', type: 'custom', icon: 'Furnace', lastVersionId: versionId, gameDir: spiel,
    javaArgs: '-Xmx4G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC', created: jetzt, lastUsed: jetzt
  };
  schreib(pfad, JSON.stringify(daten, null, 2));
  return 'ok';
}
JXA
    open -a "Minecraft" 2>/dev/null || open -b com.mojang.minecraftlauncher 2>/dev/null || true
    status 100 "Fertig!"
    meldung "Fertig. Im Minecraft Launcher:

1. Mit dem Microsoft-Konto anmelden (falls noch nicht).
2. Unten neben Spielen das Profil „Schützenfest“ wählen und Spielen.
3. Die Warnung zu Mods bestätigen, dann Mehrspieler → Schützenfest.

Beim ersten Beitreten lädt der Server fehlende Teile nach und startet das Spiel einmal neu."
}

case "$WAHL" in
    "Modrinth App") mit_modrinth ;;
    "Minecraft Launcher") mit_launcher ;;
esac
